/*
Copyright (c) 2013 Mihail Volkov

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "quickstart_unit.h"

#ifndef DISABLE_HTMD

//
// HtMdQuickStartUnit
//

HtMdQuickStartUnit::HtMdQuickStartUnit
	(int sendBufferSize,int recvBufferSize):
	mOutBufSize (sendBufferSize),
	mInBufSize (recvBufferSize),
	mOutCommandsTotalSize (0),
	mOutCommandSofar (-1),
	mConsoleInputSofar (-1),
	mClientReady (false)
{
	mConsoleInputCommand.reserve (recvBufferSize);
}

HtMdQuickStartUnit::~HtMdQuickStartUnit ()
{
	// nothing to do here yet
}

void HtMdQuickStartUnit::sendString (const std::string &outString)
{
	if (!isConnected () && !mClientReady) return;

	if (mOutBufSize>0 &&
		(int)outString.size ()+mOutCommandsTotalSize>mOutBufSize)
		emitOutputOverflowWarning ();
	else
	{
		mOutCommands.push_back (outString);
		mOutCommandsTotalSize += outString.size ();
		mOverflowWarningEmitted = false;
	}
	if (mOutCommandSofar==-1)
	{
		mOutCommandSofar = 0;
		outUnitCommandBegin (mOutCommands.front ().size ());
	}
}

void HtMdQuickStartUnit::emitOutputOverflowWarning ()
{
	if (!mOverflowWarningEmitted)
	{
		mOutCommands.push_back (this->getSentOverflowWarningString ());
		mOutCommandsTotalSize += mOutCommands.back ().size ();
		mOverflowWarningEmitted = true;
	}
}

// unit interface
void HtMdQuickStartUnit::inClientCommandBegin (int len)
{
	mConsoleInputSofar = 0;
	mConsoleInputTruncated = mInBufSize>0 && (len>mInBufSize);
	mConsoleInputCommand.clear ();
}

void HtMdQuickStartUnit::inClientCommandChunk
	(const HtMdConstChunkDesc &chunk)
{
	int i,n = chunk.getLength ();
	if (mConsoleInputSofar!=-1)
		for (i=0; i<n &&
			(mInBufSize<=0 || mConsoleInputSofar<mInBufSize); i++)
		{
			mConsoleInputCommand += chunk[i];
			mConsoleInputSofar++;
		}
}

void HtMdQuickStartUnit::inClientCommandOk ()
{
	this->onReceiveString (mConsoleInputCommand,
		mConsoleInputTruncated);
}

void HtMdQuickStartUnit::inClientCommandCancelled ()
{
	mConsoleInputSofar = 0;
	mConsoleInputCommand.clear ();
	// ^this is not actually needed, next inClientCommandChunk
	// won't come before next inClientCommandBegin
}

int HtMdQuickStartUnit::outUnitCommandChunk (HtMdChunkDesc &chunk)
{
	const std::string &cmd = mOutCommands.front ();
	int i,n = (int)cmd.size (),n1 = chunk.getLength ();
	for (i=0; i<n1 && mOutCommandSofar<n; i++)
		chunk[i] = cmd[mOutCommandSofar++];
	return i;
}

void HtMdQuickStartUnit::outUnitCommandDone ()
{
	mOutCommandsTotalSize -= mOutCommands.front ().size ();
	mOutCommands.pop_front ();
	if (mOutCommands.empty ())
		mOutCommandSofar = -1; // no more pending output
	else
	{
		// begin next output item
		mOutCommandSofar = 0;
		outUnitCommandBegin (mOutCommands.front ().size ());
	}
}

void HtMdQuickStartUnit::onClientConnected ()
{
	mClientReady = false;
}

void HtMdQuickStartUnit::onClientDisconnected ()
{
	// clear queues, since they are kinda 'already sent', and
	// no one cares if they were not actually accepted
	mOutCommandSofar = -1;
	mOutCommands.clear ();

	mClientReady = false;
}

void HtMdQuickStartUnit::onClientSuspended ()
{
	outUnitCommandCancel ();
	mOutCommands.clear ();
	mClientReady = false;
}

void HtMdQuickStartUnit::onClientTurnIn ()
{
	mClientReady = true;
}

//
// HtMdQuickFileUploader
//

HtMdQuickFileUploader::HtMdQuickFileUploader ():
	mpFile (NULL)
{
	// nothing to do here
}

HtMdQuickFileUploader::~HtMdQuickFileUploader ()
{
	cancel ();
}

void HtMdQuickFileUploader::inUploadBegin
	(const std::string &selectedName,int /*size*/)
{
	std::string actName =
		this->acceptIncomingName (mName,selectedName);
	if ((mpFile = fopen (actName.c_str (),"wb"))==NULL)
	{
		cancel ();
		mError = std::string ("can't open file ")+actName
			+" for writing";
	}
	else mName = actName;
}

void HtMdQuickFileUploader::inUploadChunk
	(const HtMdConstChunkDesc &chunk)
{
	if (fwrite (chunk,chunk.getLength (),1,mpFile)<1)
	{
		cancel ();
		mError = std::string ("can't write into file ")+mName;
	}
}

void HtMdQuickFileUploader::inUploadOk ()
{
	fclose (mpFile);
	mpFile = NULL;
	mError = "";
}

void HtMdQuickFileUploader::inUploadCancelled ()
{
	if (mpFile!=NULL)
	{
		fclose (mpFile);
		mpFile = NULL;
	}
	if (!HtMdSupervisor::isConnected ())
		mError = "client disconnected";
	// ^by the way, if you intend to print the error message via
	// something like HtMd powered debug consule, the disconnect
	// message won't be seen by the client for obvious reason ;(
}

void HtMdQuickFileUploader::beginUpload (const std::string &fileName,
	const std::string &filter)
{
	cancel ();

	mName = fileName;
	std::string suggFilter;
	if (filter=="")
	{
		// construct default filter
		unsigned firstNameChar;
		for (firstNameChar=fileName.size ();;firstNameChar--)
		{
			if (fileName[firstNameChar]=='/' ||
				fileName[firstNameChar]=='\\')
			{
				firstNameChar++;
				break;
			}
			if (firstNameChar==0) break;
		}
		suggFilter = fileName!=""
			? fileName.substr (firstNameChar)+";;All files (*)"
			: "All files (*)";
	}
	else
		suggFilter = filter;
	// hope for best, prepare for the worst
	mError = "upload aborted by the system";
	beginRequestUpload (suggFilter);
}

std::string HtMdQuickFileUploader::getErrorString ()
{
	return mError;
}


//
// HtMdQuickFileDownloader
//

HtMdQuickFileDownloader::HtMdQuickFileDownloader ():
	mpFile (NULL)
{
	// nothing to do here
}

HtMdQuickFileDownloader::~HtMdQuickFileDownloader ()
{
	cancel ();
}

void HtMdQuickFileDownloader::inDownloadBegin
	(const std::string &/*selectedName*/)
{
	fseek (mpFile,0,SEEK_SET);
}

void HtMdQuickFileDownloader::inDownloadChunk
	(HtMdChunkDesc chunk)
{
	if (fread (chunk,chunk.getLength (),1,mpFile)<1)
	{
		cancel ();
		mError = std::string ("can't read from file ")+mName;
	}
}

void HtMdQuickFileDownloader::inDownloadOk ()
{
	fclose (mpFile);
	mpFile = NULL;
	mError = "";
}

void HtMdQuickFileDownloader::inDownloadCancelled ()
{
	if (mpFile!=NULL)
	{
		fclose (mpFile);
		mpFile = NULL;
	}
	if (!HtMdSupervisor::isConnected ())
		mError = "client disconnected";
	// ^by the way, if you intend to print the error message via
	// something like HtMd powered debug consule, the disconnect
	// message won't be seen by the client for obvious reason ;(
}

bool HtMdQuickFileDownloader::beginDownload
	(const std::string &fileName,const std::string &filter)
{
	cancel ();

	mName = fileName;
	if ((mpFile = fopen (fileName.c_str (),"rb"))==NULL)
	{
		mError = std::string ("failed to open file ")+fileName;
		return false;
	}
	std::string suggFilter;
	// construct default filter
	unsigned firstNameChar;
	for (firstNameChar=fileName.size ();;firstNameChar--)
	{
		if (fileName[firstNameChar]=='/' ||
			fileName[firstNameChar]=='\\')
		{
			firstNameChar++;
			break;
		}
		if (firstNameChar==0) break;
	}
	suggFilter = filter!=""
		? fileName.substr (firstNameChar)+";;All files (*)"
		: "All files (*)";
	// hope for best, prepare for the worst
	mError = "download aborted by the system";
	// fetch assumed length of the download
	fseek (mpFile,0,SEEK_END);
	beginRequestDownload (fileName.substr (firstNameChar)
		+";"+suggFilter,ftell (mpFile));
	return true;
}

std::string HtMdQuickFileDownloader::getErrorString ()
{
	return mError;
}

#endif // DISABLE_HTMD
